<script lang="ts" setup>
import axios from 'axios';
import * as echarts from 'echarts'
import { onMounted, ref } from 'vue';
import { usePageStore } from '@/stores/counter'
usePageStore().setTitle('综合告警平台')

const alertTableBody = ref(null);
const heatmapChartDom = ref(null);
let heatmapChart = null


// API 地址配置
const ALERTMANAGER_URL = '/alert/api/v2/alerts'; // Alertmanager API
const PROMETHEUS_QUERY_URL = '/pms/api/v1/query'; // Prometheus Query API

// Prometheus 查询表达式
const SENT_QUERY = 'sum(increase(icmp_packets_sent_total{source!="", target!=""}[5m])) by (source, target)';
const RECEIVED_QUERY = 'sum(increase(icmp_packets_received_total{source!="", target!=""}[5m])) by (source, target)';

// 数据刷新间隔 (毫秒)
const updateInterval = 5000; // 每5秒刷新一次

// --- 热力图 (点阵形式) 渲染函数 ---
async function updateHeatmapChart() {
  if (!heatmapChart) return
  heatmapChart.resize()

  try {
    // 同时发起两个Prometheus查询请求
    const [sentResponse, receivedResponse] = await Promise.all([
      axios.get(PROMETHEUS_QUERY_URL, { params: { query: SENT_QUERY } }),
      axios.get(PROMETHEUS_QUERY_URL, { params: { query: RECEIVED_QUERY } })
    ]);

    const sentResult = sentResponse.data.data.result || [];
    const receivedResult = receivedResponse.data.data.result || [];

    // 将接收到的数据转换为更易于处理的Map结构
    const receivedMap = new Map(); // Key: "source-target", Value: received_value
    receivedResult.forEach(item => {
      const source = item.metric.source;
      const target = item.metric.target;
      if (source && target) {
        receivedMap.set(`${source}-${target}`, parseFloat(item.value[1]));
      }
    });

    const sources = [];
    const targets = [];
    const scatterData = []; // [targetIndex, sourceIndex, value (丢包率), originalSent, originalReceived]

    // 遍历发送数据，计算丢包率
    sentResult.forEach(item => {
      const source = item.metric.source;
      const target = item.metric.target;
      const sent = parseFloat(item.value[1]);
      const received = receivedMap.get(`${source}-${target}`) || 0; // 如果没有对应的接收数据，默认为0

      if (source && target) {
        if (!sources.includes(source)) sources.push(source);
        if (!targets.includes(target)) targets.push(target);

        let packetLoss = 0;
        if (sent > 0) {
          packetLoss = 1 - (received / sent);
          if (packetLoss < 0) packetLoss = 0; // 确保不出现负值
        } else {
          packetLoss = NaN; // sent为0时设为NaN，ECharts visualMap outOfRange会处理
        }

        // scatterData: [x-index (target), y-index (source), value (丢包率), sent, received]
        scatterData.push([
          targets.indexOf(target), // X轴为Target
          sources.indexOf(source), // Y轴为Source
          packetLoss,
          sent,
          received
        ]);
      }
    });

    // 如果没有数据，显示提示
    if (sources.length === 0 || targets.length === 0) {
      heatmapChart.clear(); // 清空之前的图表
      heatmapChart.setOption({
        title: {
          text: '暂无业务网连通性数据',
          left: 'center',
          top: 'middle',
          textStyle: {
            color: '#aaa',
            fontSize: 20
          }
        }
      });
      return;
    }

    // 排序，保持一致性
    sources.sort();
    targets.sort();

    // 重新映射 scatterData 的索引，因为 sources 和 targets 可能在构建过程中动态添加且未排序
    const finalScatterData = scatterData.map(item => {
      // item[0] is original targetIndex, item[1] is original sourceIndex
      // We need to get the original names first, then find their new sorted indices
      const originalSource = sources[item[1]];
      const originalTarget = targets[item[0]];

      return [
        targets.indexOf(originalTarget), // Correct X-axis index after sorting targets
        sources.indexOf(originalSource), // Correct Y-axis index after sorting sources
        item[2], // packetLoss
        // item[3], // sent
        // item[4]  // received
      ];
    });

    const option = {
      tooltip: {
        position: 'top',
        formatter: function (params) {
          // params.value[0] is targetIndex, params.value[1] is sourceIndex
          const source = sources[params.value[1]];
          const target = targets[params.value[0]];
          const loss = params.value[2];
          const sent = params.value[3];
          const received = params.value[4];

          let lossText;
          if (isNaN(loss)) {
            lossText = 'N/A (无发送数据)';
          } else {
            lossText = (loss * 100).toFixed(2) + '%';
          }
          return `
                        来源: <b>${source}</b><br/>
                        目标: <b>${target}</b><br/>
                        发送: ${sent} 包<br/>
                        接收: ${received} 包<br/>
                        丢包率: <b style="color:${params.color};">${lossText}</b>
                    `;
        }
      },
      grid: {
        left: '5%',
        right: '5%',
        top: '5%',
        bottom: '15%',
        containLabel: true
      },
      xAxis: {
        type: 'category',
        data: targets, // 使用排序后的targets
        axisLabel: {
          show: false
        },
        axisLine: {
          show: false
        },
        axisTick: {
          show: false
        },
        splitLine: {
          show: true,
          lineStyle: {
            color: '#333'
          }
        }
      },
      yAxis: {
        type: 'category',
        data: sources, // 使用排序后的sources
        axisLabel: {
          show: false
        },
        axisLine: {
          show: false
        },
        axisTick: {
          show: false
        },
        splitLine: {
          show: true,
          lineStyle: {
            color: '#333'
          }
        }
      },
      visualMap: {
        show: false,
        min: 0,
        max: 1,
        calculable: true,
        orient: 'horizontal',
        left: 'center',
        bottom: '0%',
        text: ['高丢包', '低丢包'],
        inRange: {
          // 修正颜色顺序：0%丢包 (min) 对应绿色，100%丢包 (max) 对应红色
          color: ['#2f9011', '#c7c427', '#E00000'] // 绿色, 黄色, 红色
        },
        outOfRange: {
          color: '#888888' // NaN值显示为灰色
        },
        textStyle: {
          color: '#f0f0f0'
        },
        formatter: function (value) {
          // 格式化visualMap的标签
          return (value * 100).toFixed(0) + '%';
        }
      },
      series: [{
        name: '丢包率',
        type: 'scatter',
        data: finalScatterData,
        symbol: 'circle', // 改为圆形
        symbolSize: 4, // 调小点尺寸，例如 8
        label: {
          show: false,
        },
        emphasis: {
          itemStyle: {
            shadowBlur: 10,
            shadowColor: 'rgba(0, 0, 0, 0.5)'
          }
        },
        itemStyle: {
          borderColor: '#555', // 可以保持边框，让点更明显
          borderWidth: 0 // 或者设置为0，使其更像纯色点
        }
      }]
    };

    heatmapChart.setOption(option, true);

  } catch (error) {
    console.error('获取热力图数据失败:', error);
    heatmapChart.clear();
    heatmapChart.setOption({
      title: {
        text: `加载热力图数据失败！\n请检查Prometheus服务和CORS配置。\n错误信息: ${error.message}`,
        left: 'center',
        top: 'middle',
        textStyle: {
          color: 'red',
          fontSize: 16
        }
      }
    });
  }
}


// --- 告警表格渲染函数 ---
async function fetchAlerts() {
  if (!alertTableBody.value) return
  try {
    const response = await axios.get(ALERTMANAGER_URL);
    const alerts = response.data; // Alertmanager /api/v2/alerts 直接返回数组

    alertTableBody.value.innerHTML = ''; // 清空现有数据

    if (!alerts || alerts.length === 0) {
      const row = alertTableBody.value.insertRow();
      const cell = row.insertCell();
      cell.colSpan = 5;
      cell.textContent = '当前没有告警信息。';
      cell.style.textAlign = 'center';
      cell.style.padding = '20px';
      return;
    }

    // 过滤掉非活动状态的告警 (如果Alertmanager返回了所有状态的告警)
    const activeAlerts = alerts.filter(alert => alert.status.state === 'active');

    if (activeAlerts.length === 0) {
      const row = alertTableBody.value.insertRow();
      const cell = row.insertCell();
      cell.colSpan = 5;
      cell.textContent = '当前没有活动告警信息。';
      cell.style.textAlign = 'center';
      cell.style.padding = '20px';
      return;
    }

    activeAlerts.forEach(alert => {
      const row = alertTableBody.value.insertRow();
      row.insertCell().textContent = alert.labels.alertname || 'N/A';

      const severity = alert.labels.severity || 'info';
      const severityCell = row.insertCell();
      severityCell.textContent = severity;
      severityCell.classList.add(`severity-${severity}`);

      row.insertCell().textContent = alert.labels.instance || 'N/A';
      row.insertCell().textContent = alert.annotations.summary || 'N/A';
      row.insertCell().textContent = new Date(alert.startsAt).toLocaleString();
    });
  } catch (error) {
    console.error('获取告警信息失败:', error);
    alertTableBody.value.innerHTML = `<tr><td colspan="5" style="text-align:center;color:red;">加载告警数据失败！<br/>请检查Alertmanager服务和CORS配置。<br/>错误信息: ${error.message}</td></tr>`;
  }
}

onMounted(() => {

  heatmapChart = echarts.init(heatmapChartDom.value);



  // 初始化加载数据
  fetchAlerts();
  updateHeatmapChart();

  // 设置定时刷新
  setInterval(fetchAlerts, updateInterval);
  setInterval(updateHeatmapChart, updateInterval);
})
</script>
<template>
  <div class="dashboard-container">
    <main class="dashboard-content">
      <div class="panel alert-panel">
        <h2>监控告警</h2>
        <div class="alert-table-container">
          <table id="alertTable">
            <thead>
              <tr>
                <th>告警名称</th>
                <th>级别</th>
                <th>实例</th>
                <th>摘要</th>
                <th>开始时间</th>
              </tr>
            </thead>
            <tbody id="alertTableBody" ref="alertTableBody">
              <!-- 告警数据将在这里动态加载 -->
            </tbody>
          </table>
        </div>
      </div>
      <div class="panel heatmap-panel">
        <h2>业务网连通性</h2>
        <div id="heatmapChart" ref="heatmapChartDom" class="chart-container"></div>
      </div>
    </main>
  </div>
</template>
<style>
.dashboard-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  /* 全屏高度 */
}

.dashboard-header {
  text-align: center;
  padding: 20px;
  background-color: rgba(0, 0, 0, 0.3);
  border-bottom: 1px solid #333;
  font-size: 1.5em;
  color: #ffd700;
  /* 标题颜色 */
  flex-shrink: 0;
  /* 防止标题被压缩 */
}

.dashboard-content {
  flex-grow: 1;
  /* 占据剩余空间 */
  display: flex;
  padding: 20px;
  gap: 20px;
  /* 面板之间的间距 */
  overflow: hidden;
  /* 防止内容溢出，内部面板再控制滚动 */
}

.panel {
  flex: 1;
  /* 左右面板各占一半宽度 */
  background-color: rgba(0, 0, 0, 0.4);
  border-radius: 8px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
}

.panel h2 {
  text-align: center;
  margin-top: 0;
  margin-bottom: 20px;
  color: #00bfff;
  /* 面板标题颜色 */
  font-size: 1.2em;
}

/* 告警表格样式 */
.alert-table-container {
  flex-grow: 1;
  overflow-y: auto;
  /* 表格内容过多时垂直滚动 */
}

#alertTable {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9em;
}

#alertTable th,
#alertTable td {
  padding: 10px 15px;
  text-align: left;
  border-bottom: 1px solid #333;
}

#alertTable th {
  background-color: rgba(0, 0, 0, 0.5);
  color: #aaa;
  font-weight: bold;
  position: sticky;
  top: 0;
  z-index: 1;
}

#alertTable tbody tr:nth-child(even) {
  background-color: rgba(0, 0, 0, 0.2);
}

#alertTable tbody tr:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

/* 告警级别颜色 */
.severity-critical {
  color: #ff4d4f;
  /* 红色 */
  font-weight: bold;
}

.severity-warning {
  color: #faad14;
  /* 橙色 */
}

.severity-info {
  color: #1890ff;
  /* 蓝色 */
}

/* 热力图容器样式 */
.chart-container {
  flex-grow: 1;
  /* 占据剩余空间 */
  min-height: 200px;
  /* 最小高度 */
}
</style>
